/*@parser.c::*/
/** 
 * Simplified Pascal in the EBNF:
 * 
 * program -> PROGRAM ID ';' block '.'
 * 
 * block -> declarations modules stmtblock
 * 
 * declarations -> 
 *    [CONST ID = constant';' {ID = constant';'}]
 *    [TYPE ID = type';' {ID = type';'}]
 *    [VAR idlist ':' type';' {idlist ':' type';'}]
 *    
 *
 * modules -> {procedure | function}
 * 
 * procedure -> PROCEDURE ID formalparm';' declarations stmtblock ';'
 * 
 * function -> FUNCTION ID formalparm ':' typeid ';'declarations stmtblock ';'
 * 
 * stmtblock -> BEGIN stmtlist END
 * 
 * stmtlist -> stmt {';' stmt}
 * 
 * stmt -> stmtblock | ifstmt | whilestmt | repstmt | forstmt
 *      | gotostmt | casestmt | idstmt
 */

#include <string.h>
#include <stdio.h>
#include "lexer.h"
#include "parser.h"
#include "symtab.h"
#include "pseudocode.h"
#include "keywords.h"
#include "tokens.h"

#define MAXSYMLIST      0x10000
#define MAXIDSIZ        32
char symlist[MAXSYMLIST][MAXIDSIZ];
int sympos,procpos,funcpos,idposi,idposf;

int gLabel = 1;
int otype,dtype,returnedtype;
int parfunc = 0;
int offset,varOffset = 0,funcOffset = 0;
int symbol_entry;
int progSect;

int relopused;
char floatval[10];

/* program -> PROGRAM ID ';' block '.'*/
void program(void)
{
  match(PROGRAM); 
  match(ID);
  match(';');
  /** */
  progSect = PROGRAM;
  fprintf(stdout, "\n\t.section .rodata\n");
  /** */
  block();
  match('.');
  /** */
  exitgen();
  /** */
}

/* block -> declarations modules stmtblock*/
void block(void)
{
  declarations();
  /** */fprintf(stdout, "\n\t.section .text\n");/** */
  modules();
  /** */funcgen("_start");/** */
  stmtblock();
}

/* declarations -> 
*    [VAR idlist ':' type';' {idlist ':' type';'}]*/
void declarations(void)
{
  if(lookahead==VAR){
    /** */
    otype = VARTYPE;
    if(progSect == PROGRAM) fprintf(stdout, "\n\t.section .bss\n");
    /** */
    match(VAR); 
rep_idlist:
    /** */sympos=0/** */;
    idlist();match(':');type();match(';');
    if(lookahead==ID)goto rep_idlist;
  }
}

/* modules -> {procedure | function} */
void modules(void)
{
  while(lookahead==PROCEDURE || lookahead==FUNCTION){
    if(lookahead==PROCEDURE) procedure();
    else function();
    /** */
    funcOffset = 0;
    varOffset = 0;
    symtab_removelocals();
    /** */
  }
}

/* procedure -> PROCEDURE ID formalparm';' declarations stmtblock ';' */
void procedure(void)
{
  int i;
  match(PROCEDURE);
  /** **/
  parfunc = 0;
  procpos = sympos;
  progSect = PROCEDURE;
  strcpy(symlist[sympos++],lexeme);
  funcgen(lexeme);
  /** **/
  match(ID);
  formalparm();
  match(';');
  /** */
  otype = PROCTYPE;
  if(symtab_lookup(symlist[procpos])) {
    fprintf(stderr,"symbol \"%s\" already declared\n",
      symlist[procpos]);
  }else{
    funcOffset += 4;
    symtab_insert(symlist[procpos],otype,0,funcOffset);
  }
  
  if(parfunc){
    int i;
    for(i=idposf;i>=idposi-1;i--) {
      symbol_entry = symtab_lookup(symlist[i]);
      switch(getDtype(symbol_entry)){
	case INTEGER:
	case REAL:
	  funcOffset += 4;
	  setOffset(symbol_entry,funcOffset);
	  break;
      }
    }
  }
  /** */
  declarations();
  stmtblock();
  match(';');
  /** */
  endfuncgen();
  retgen();
  /** */
}

/* function -> FUNCTION ID formalparm ':'  typeid ';' declarations stmtblock ';' */
void function(void)
{
  match(FUNCTION);
  /** */
  parfunc = 0;
  funcpos = sympos;
  progSect = FUNCTION;
  strcpy(symlist[sympos++],lexeme);
  funcgen(lexeme);
  /** **/
  match(ID);
  formalparm();
  match(':');
  /** */otype = FUNCTYPE;/** */
  type(); 
  /** */
  if(parfunc){
    int i;
    for(i=idposf;i>=idposi-1;i--) {
      symbol_entry = symtab_lookup(symlist[i]);
      switch(getDtype(symbol_entry)){
	case INTEGER:
	case REAL:
	  funcOffset += 4;
	  setOffset(symbol_entry,funcOffset);
	  break;
      }
    }
  }
  /** */
  match(';');
  declarations();
  stmtblock();
  match(';');
  /** */
  endfuncgen();
  retgen();
  /** */
}

/* formalparm -> '(' parlist ')' | "" */
void formalparm(void){
  if(lookahead =='('){
    match('(');
    parlist();
    match(')');
  }
}

/* parlist -> pardecl {';' pardecl}*/
void parlist(void){
  pardecl();
  while(lookahead == ';'){
    match(';');
    pardecl();
  }
}

/*pardecl -> [VAR] idlist ':' type | heading */
void pardecl(void){
  /** */otype = PARMTYPE;/** */
  switch(lookahead){
    case VAR:
      match(VAR);
      idlist();
      match(':');
      type();
    break;
    case ID:
      idlist();
      match(':');
      type();
    break;
    
    case PROCEDURE:
    case FUNCTION:
      heading();
    break;
  }
}

/* heading -> procedureheader | functionheader*/
void heading(void){
  if(lookahead==PROCEDURE) procedureheader();
  else functionheader();
}

/* procedureheader -> PROCEDURE ID formalparm */
void procedureheader(void){
  match(PROCEDURE);match(ID);formalparm();
}

/* functionheader -> FUNCTION ID formalparm ':' typeid */
void functionheader(void){
  match(FUNCTION);match(ID);formalparm();match(':');type();
}

/* idlist -> ID {','ID}*/
void idlist(void)
{
   /** */parfunc = 1;/** */
  /** */idposi = sympos;/** */
  /** **/strcpy(symlist[sympos++],lexeme);/** **/
  /** **/if(progSect == PROGRAM) fprintf(stdout, "\t .common %s_,4\n",lexeme);/** **/
  match(ID);
  while(lookahead==','){
    match(',');
    /** **/strcpy(symlist[sympos++],lexeme);/** **/
    /** **/if(progSect == PROGRAM) fprintf(stdout, "\t.common %s_,4\n",lexeme);/** **/
    match(ID);
  }
  /** */idposf = sympos;/** */
}

/* type -> INTEGER | REAL */
void type(void)
{
  int i;
  switch(lookahead){
    case INTEGER:
      match(dtype=INTEGER);
      break;
    default:
      match(dtype=REAL);
  }
  
  /** */
  switch(otype){
    case VARTYPE:
      for(i=idposi;i<idposf;i++) {
	if(symtab_lookup(symlist[i])) {
	  fprintf(stderr,"symbol \"%s\" already declared\n",
	    symlist[i]);
	}else{
	  switch(dtype){
	    case INTEGER:
	    case REAL:
	      if(progSect != PROGRAM) varOffset -= 4;
	      break;
	  }
	  symtab_insert(symlist[i],otype,dtype,varOffset);
	}
      }
      break;
    case PARMTYPE:
      for(i=idposi;i<idposf;i++) {
	if(symtab_lookup(symlist[i])) {
	  fprintf(stderr,"symbol \"%s\" already declared\n",
	    symlist[i]);
	}else{
	  symtab_insert(symlist[i],otype,dtype,0); //inserção após definir o retorno de função
	}
      }
      break;
    case FUNCTYPE:
      if(symtab_lookup(symlist[funcpos])) {
	fprintf(stderr,"symbol \"%s\" already declared\n",
	  symlist[funcpos]);
      }else{
	switch(dtype){
	  case INTEGER:
	  case REAL:
	    funcOffset += 4;
	    break;
	}
	symtab_insert(symlist[funcpos],otype,dtype,funcOffset);
      }
      break;
  }
  /** */
}

/* stmt -> stmtblock | ifstmt | whilestmt | repstmt | idstmt | "" */
void stmt(void)
{
  switch(lookahead){
    case BEGIN:
      stmtblock();
      break;
    case IF:
      ifstmt();
      break;
    case WHILE:
      whilestmt();
      break;
    case REPEAT:
      repstmt();
      break;
    case ID:
      idstmt();
      break;
  }
}

/* stmtblock -> BEGIN stmtlist END */
void stmtblock(void)
{
  match(BEGIN);stmtlist();match(END);
}

/* stmtlist -> stmt {';' stmt} */
void stmtlist(void)
{
  stmt();
  while(lookahead==';'){
    match(';');stmt();
  }
}

/* ifstmt -> IF expression THEN stmt [ELSE stmt] */
void ifstmt(void)
{
  int lbl = gLabel++;
  int lbl2 = lbl;
  match(IF);
  expression();
  match(THEN);
  /** */jzgen(lbl,relopused);/** */
  stmt();
  if(lookahead==ELSE) {
    lbl2 = gLabel++;
    match(ELSE);
    /** */jmpgen(lbl2);/** */
    /** */lblgen(lbl);/** */
    stmt();
  }
  /** */lblgen(lbl2);/** */
}

/* whilestmt -> WHILE expression DO stmt */
void whilestmt(void)
{
  int lblHead = gLabel++;
  int lblTail = gLabel++;
  match(WHILE);
  /** */lblgen(lblHead);/** */
  expression();
  match(DO);
  /** */jzgen(lblTail,relopused);/** */
  stmt();
  /** */jmpgen(lblHead);/** */
  /** */lblgen(lblTail);/** */
}

/* repstmt -> REPEAT stmt {';' stmt} UNTIL expression */
void repstmt(void)
{
  int lbl = gLabel++;
  match(REPEAT);
  /** */lblgen(lbl);/** */
  stmt();
  while(lookahead == ';'){
    match(';'); stmt();
  }
  match(UNTIL); expression();
  /** */jzgen(lbl,relopused);/** */
}

/* idstmt -> ID assgnmt expression */
void idstmt(void)
{
  /** **/
  if((symbol_entry = symtab_lookup(lexeme))==0){
    fprintf(stderr,"semantic: symbol not found");
  }else{
    switch(getOtype(symbol_entry)){
      case VARTYPE:/** this is a variable **/
      case FUNCTYPE:/** this is a function call **/
      case PARMTYPE:
        break;
      default:
	fprintf(stderr,"symbol in ilegal context");
    }
  }
  char symbol[MAXLEN];
  strcpy(symbol,lexeme);
  /** **/
  match(ID); match(ASSGNMT); expression();
  offset=getOffset(symbol_entry);
  if(offset==0){
    /** **/
    if(acctype == FLOATCONV) /** **/fprintf(stdout,"\tfstps %%eax\n",offset);/** **/
    fprintf(stdout,"\tmovl %%eax,%s_\n",symbol);
    /** **/
  }else{
    /** **/
    if(acctype == FLOATCONV) /** **/fprintf(stdout,"\tfstps %%eax\n",offset);/** **/
    fprintf(stdout,"\tmovl %%eax,%d(%%ebp)\n",offset);
    /** **/
  }
}


/* relationalop -> '=' | '<' | '>' | GEQ | LEQ | NEQ */
isrelationalop(token_t token)
{
  switch(token){
    case '=':
    case '>':
    case '<':
    case GEQ:
    case LEQ:
    case NEQ:
      return token;
  }
  return 0;
}

/* addop -> '+' | '-' | OR */
isaddop(token_t token)
{
  switch(token){
    case '+':
    case '-':
    case OR:
      return token;
  }
  return 0;
}

/* mulop -> '*' | '/' | AND | DIV | MOD */
ismulop(token_t token)
{
  switch(token){
    case '*':
    case '/':
    case AND:
    case DIV:
    case MOD:
      return token;
  }
  return 0;
}

/* expression -> expr [relationalop expr] */
void expression(void)
{
  /** an inherited attribute is get from the left side of
   * an assignment if any **/
  expr();
  if((relop = isrelationalop(lookahead))){
    /** */fprintf(stdout, "\tpushl\t%%eax\n");/** */
    match(lookahead); expr();
    /** */relopgen();/** */
    relopused = relop;
  }
  /** at this point we get a resulting data type as a
   * synthesized attribute **/
  /** */acctype = 0;/** */
}

/* expr -> [-] term {addop term} */
void expr(void)
{
  if(lookahead=='-'){
    match('-');
  }
  term();
  while((addop = isaddop(lookahead))){
    /** */fprintf(stdout, "\tpushl\t%%eax\n");/** */
    match(lookahead); term();
    /** */addopgen(addop);/** */
  }
}

/* term -> factor {mulop factor} */
void term(void)
{
  factor();
  while((mulop = ismulop(lookahead))){
    /** */fprintf(stdout, "\tpushl\t%%eax\n");/** */
    match(lookahead); factor();
    /** */mulopgen(mulop);/** */
  }
}

/* factor -> UINT | FLOAT | ID ['(' exprlist ')'] | '(' expr ')' | NOT factor */
void factor(void){
    switch(lookahead){
      case UINT:
        /** **/
        if((acctype = max(acctype, INTCONV)) == INTCONV){
	  (strcmp(lexeme,"0") < 0) ? fprintf(stdout,"\tmovl $0,%%eax\n") : fprintf(stdout,"\tmovl $%s,%%eax\n",lexeme);
	}else if(acctype == FLOATCONV){
	  (strcmp(lexeme,"0") < 0) ? fprintf(stdout,"\tmovl $%s,%%eax\n",convertfloat2ieee("0",floatval)) : fprintf(stdout,"\tmovl $%s,%%eax\n",convertfloat2ieee(lexeme,floatval));
	  fprintf(stdout,"\tflds %%eax\n");
	}
        match(UINT);break;
      case FLOAT:
        /** **/
        acctype = max(acctype, FLOATCONV);
	fprintf(stdout,"\tmovl $%s,%%eax\n",convertfloat2ieee(lexeme,floatval));
	fprintf(stdout,"\tflds %%eax\n");
        match(FLOAT);break;
      case ID:
        /** in this context the symbol must be a variable or
         * a function using a symtab_lookup call **/
        /** **/
        if((symbol_entry = symtab_lookup(lexeme))==0){
          fprintf(stderr,"semantic: symbol not found");
        }else{
          switch(getOtype(symbol_entry)){
            case VARTYPE:/** this is a variable **/
	    case PARMTYPE:
	      switch(getDtype(symbol_entry)){
		case INTEGER:
		  acctype = max(acctype, INTCONV);
		  break;
		case REAL:
		  acctype = max(acctype, FLOATCONV);
		  break;
	      }
              break;
            case FUNCTYPE:/** this is a function call **/
	      switch(getDtype(symbol_entry)){
		case INTEGER:
		  acctype = max(acctype, INTCONV);
		  break;
		case REAL:
		  acctype = max(acctype, FLOATCONV);
		  break;
	      }
              break;
            default:
              fprintf(stderr,"symbol in ilegal context");
          }
        }
        char symbol[MAXLEN];
	strcpy(symbol,lexeme);
        /** **/
        match(ID);
        if(lookahead=='('){
          /** this is the context for function call **/
          match('(');exprlist();match(')');
          /** subroutine call is here **/
	  fprintf(stdout,"\tcall %s\n",symbol);
        }else{
          /** this is the context for simple variable **/
          /** **/
          offset=getOffset(symbol_entry);
          if(offset==0){
            /** the variable is global, then its identification
             * itself is the variable address **/
            /** **/
            fprintf(stdout,"\tmovl %s_,%%eax\n",symbol);
	    if(acctype == FLOATCONV) /** **/fprintf(stdout,"\tflds %%eax\n",offset);/** **/
            /** **/
          }else{
            /** in this context all symbols are looked up in the
             symbol table in order to compose the stack address:
                <offset>(%ebp)
             for instance, a variable at offset -4 would be
               written as
                -4(%ebp)
               a second variable with size 4 would be translated
               to
                -8(%ebp)
               on the other hand, a parameter in the first slot
               would be interpreted as
                8(%ebp)
            **/
	    
            /** **/fprintf(stdout,"\tmovl %d(%%ebp),%%eax\n",offset);/** **/
	    if(acctype == FLOATCONV) /** **/fprintf(stdout,"\tflds %%eax\n",offset);/** **/
          }
        }
        break;
      case '(':
        match('(');
        /** **/returnedtype=0;/** **/
        expression();
        /** **/acctype = max(acctype, returnedtype);/** **/
        match(')');
        /** at this point, expression has synthesized an
         * attribute storing the resulting data type from the
         * expression. however, the just calculated attribute
         * might be greater than the l-attribute along the
         * current expression, so that a promotion must occur
         **/
         if(acctype == FLOATCONV) /** **/fprintf(stdout,"\tflds %%eax\n",offset);/** **/
        break;
      default:
        match(NOT);factor();
    }
}
/* exprlist -> expression {',' expression} */
void exprlist(void)
{
  /** this is the context for passing parameters to procedures
   * or functions */
  expression();
  /** expression result stored in accumulator **/
  /** push the accumulator onto the stack **/
  /** **/fprintf(stdout,"\tpushl %%eax\n");/** **/
  while(lookahead==','){
    match(',');
    expression();
    /** expression result stored in accumulator **/
    /** push the accumulator onto the stack **/
    /** **/fprintf(stdout,"\tpushl %%eax\n");/** **/
  }
}
